package model

import (
	"context"
	"github.com/zeromicro/go-zero/core/stores/cache"
	"github.com/zeromicro/go-zero/core/stores/monc"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"time"
)

var _ RoleModel = (*customRoleModel)(nil)

type (
	// RoleModel is an interface to be customized, add more methods here,
	// and implement the added methods in customRoleModel.
	RoleModel interface {
		roleModel

		InsertByName(ctx context.Context, data *Role) error
		FindOneByName(ctx context.Context, name string) (*Role, error)
		UpdateByName(ctx context.Context, data *Role) (*mongo.UpdateResult, error)
		DeleteByName(ctx context.Context, name string) (int64, error)
	}

	customRoleModel struct {
		*defaultRoleModel
	}
)

func (m *customRoleModel) InsertByName(ctx context.Context, data *Role) error {
	if data.ID.IsZero() {
		data.ID = primitive.NewObjectID()
		data.CreateAt = time.Now()
		data.UpdateAt = time.Now()
	}

	key := prefixRoleCacheKey + data.Name
	_, err := m.conn.InsertOne(ctx, key, data)
	return err
}

func (m *customRoleModel) FindOneByName(ctx context.Context, name string) (*Role, error) {
	key := prefixRoleCacheKey + name
	var data Role
	err := m.conn.FindOne(ctx, key, &data, bson.M{"name": name})
	switch err {
	case nil:
		return &data, nil
	case monc.ErrNotFound:
		return nil, ErrNotFound
	default:
		return nil, err
	}
}

func (m *customRoleModel) UpdateByName(ctx context.Context, data *Role) (*mongo.UpdateResult, error) {
	data.UpdateAt = time.Now()
	key := prefixRoleCacheKey + data.Name
	return m.conn.UpdateOne(ctx, key, bson.M{"name": data.Name}, bson.M{"$set": data})
}

func (m *customRoleModel) DeleteByName(ctx context.Context, name string) (int64, error) {
	key := prefixRoleCacheKey + name
	return m.conn.DeleteOne(ctx, key, bson.M{"name": name})
}

// NewRoleModel returns a model for the mongo.
func NewRoleModel(url, db, collection string, c cache.CacheConf) RoleModel {
	conn := monc.MustNewModel(url, db, collection, c)
	return &customRoleModel{
		defaultRoleModel: newDefaultRoleModel(conn),
	}
}
